#ifndef __M_DB_H__
#define __M_DB_H__
#include "util.hpp"
#include <mutex>
#include <cassert>

class user_table
{
private:
    MYSQL *_mysql;
    std::mutex _mutex;

public:
    user_table(const std::string &host,
               const std::string &username,
               const std::string &password,
               const std::string &dbname,
               uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(host, username, password, dbname, port);
        assert(_mysql != NULL);
    }
    ~user_table()
    {
        mysql_util::mysql_destroy(_mysql);
        _mysql = NULL;
    }
    // 添加新用户
    bool insert(Json::Value &user)
    {
        if (user["password"].isNull() || user["username"].isNull())
        {
            DLOG("please input PASSWORD or USERNAME");
            return false;
        }
        char username[256] = {0}, password[256] = {0};
        mysql_real_escape_string(_mysql, username, user["username"].asCString(), user["username"].asString().size());
        mysql_real_escape_string(_mysql, password, user["password"].asCString(), user["password"].asString().size());
        char sql[2048] = {0};
        snprintf(sql, sizeof(sql),
                 "INSERT INTO user(id, username, password, score, total_count, win_count) "
                 "VALUES (NULL, '%s', '%s', 1000, 0, 0);",
                 username, password);
        if (!mysql_util::mysql_exec(_mysql, sql))
        {
            ELOG("insert user info failed! sql = %s", sql);
            return false;
        }
        ILOG("insert user success: username=%s", username);
        return true;
    }
    // 填写json表
    void fill_user_from_row(MYSQL_RES *res, Json::Value &user)
    {
        MYSQL_ROW row = mysql_fetch_row(res);
        if (row == nullptr)
            return;
        user["id"] = (Json::UInt64)std::stoull(row[0]);
        user["score"] = (Json::UInt64)std::stoull(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
    }
    // 登录验证
    bool login(Json::Value &user)
    {
        if (user["password"].isNull() || user["username"].isNull())
        {
            DLOG("please input PASSWORD or USERNAME");
            return false;
        }
        char sql[2048] = {0};
        char username[256], password[256];
        mysql_real_escape_string(_mysql, username, user["username"].asCString(), user["username"].asString().size());
        mysql_real_escape_string(_mysql, password, user["password"].asCString(), user["password"].asString().size());
        snprintf(sql, sizeof(sql),
                 "SELECT id, score, total_count, win_count FROM user WHERE username='%s' AND password='%s';",
                 username, password);
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("user login failed, sql=%s", sql);
                return false;
            }
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                ELOG("mysql_store_result returned NULL, query=%s", sql);
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            ILOG("user not found: username=%s", user["username"].asCString());
            mysql_free_result(res);
            return false;
        }
        else if (row_num > 1)
        {
            ELOG("multiple users found for username=%s!", user["username"].asCString());
            mysql_free_result(res);
            return false;
        }
        fill_user_from_row(res, user);
        mysql_free_result(res);
        return true;
    }
    // 通过用户名获取用户信息
    bool select_by_name(const std::string &name, Json::Value &user)
    {
        char sql[2048] = {0}, username[256] = {0};
        mysql_real_escape_string(_mysql, username, name.c_str(), name.size());
        snprintf(sql, sizeof(sql),
                 "select id, score, total_count, win_count from user where username='%s';",
                 username);
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("get user by name failed, sql=%s", sql);
                return false;
            }
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                ELOG("mysql_store_result returned NULL, query=%s", sql);
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            ILOG("user not found: username=%s", user["username"].asCString());
            mysql_free_result(res);
            return false;
        }
        else if (row_num > 1)
        {
            ELOG("multiple users found for username=%s!", user["username"].asCString());
            mysql_free_result(res);
            return false;
        }
        fill_user_from_row(res, user);
        mysql_free_result(res);
        return true;
    }
    // 通过用户ID获取用户信息
    bool select_by_id(uint64_t id, Json::Value &user)
    {
#define USER_BY_ID "select username, score, total_count, win_count from user where id=%llu;"
        char sql[2048] = {0};
        snprintf(sql, sizeof(sql), USER_BY_ID, (unsigned long long)id);
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("get user by id failed, sql=%s", sql);
                return false;
            }
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                ELOG("mysql_store_result returned NULL, query=%s", sql);
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            ILOG("user not found: id=%llu", (unsigned long long)id);
            mysql_free_result(res);
            return false;
        }
        else if (row_num > 1)
        {
            ELOG("multiple users found for id=%llu!", (unsigned long long)id);
            mysql_free_result(res);
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)id;
        user["username"] = row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }
    // 胜利时天梯分数增加50分，战斗场次增加1，胜利场次增加1
    bool win(uint64_t id)
    {
#define USER_WIN "update user set score=score+50, total_count=total_count+1, win_count=win_count+1 where id=%llu;"
        char sql[2048] = {0};
        snprintf(sql, sizeof(sql), USER_WIN, (unsigned long long)id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update win user info failed, sql = %s", sql);
            return false;
        }
        return true;
    }
    // 失败时天梯分数减少50，战斗场次增加1，其他不变
    bool lose(uint64_t id)
    {
#define USER_LOSE "update user set score=GREATEST(score-50,0), total_count=total_count+1 where id=%llu;"
        char sql[2048] = {0};
        snprintf(sql, sizeof(sql), USER_LOSE, (unsigned long long)id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update lose user info failed, sql = %s", sql);
            return false;
        }
        return true;
    }
};
#endif
